package userInterface.graph;

import com.mxgraph.model.*;
import com.mxgraph.swing.*;
import com.mxgraph.util.*;
import com.mxgraph.view.*;
import java.awt.*;
import java.awt.event.*;
import java.io.Serializable;
import java.util.Arrays;
import javax.swing.*;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import logging.Logger;
import userInterface.Panels.*;

public class PanelGraph extends PanelBase
{
    private static final long serialVersionUID = 1L;
    
    private ActionListener graphActionListener;
    private MouseAdapter graphMouseAdapter;
    
    private GridBagLayout gblGraph;
    
    private JButton btnAdd;
    private JButton btnRemove;
    private JButton btnCopy;
    private JButton btnPaste;
    private mxGraph graph;
    private mxGraphComponent graphComponent;
    
    private mxGeometry inGeometry;
    private mxGeometry outGeometry;
    
    private Element inPort;
    private Element outPort;
    
    public PanelGraph()
    {
        graphActionListener = new GraphActionListener();
        graphMouseAdapter = new GraphMouseAdapter();
        
        gblGraph = new GridBagLayout();
        gblGraph.columnWeights = new double[] { 0.0, 0.0, 0.0, 0.0 };
        gblGraph.rowWeights = new double[] { 0.0, 1.0 };
        setLayout(gblGraph);

        btnAdd = new JButton();
        btnAdd.setIcon(new ImageIcon(getClass().getResource("/images/add.png")));
        btnAdd.setToolTipText("Add a new workflow");
        btnAdd.setMnemonic(KeyEvent.VK_N);
        btnAdd.setActionCommand("Add");
        btnAdd.addActionListener(graphActionListener);
        GridBagConstraints gbc_btnAdd = new GridBagConstraints();
        gbc_btnAdd.anchor = GridBagConstraints.WEST;
        gbc_btnAdd.insets = new Insets(0, 10, 5, 30);
        gbc_btnAdd.gridx = 0;
        gbc_btnAdd.gridy = 0;
        add(btnAdd, gbc_btnAdd);
        
        btnRemove = new JButton();
        btnRemove.setIcon(new ImageIcon(getClass().getResource("/images/remove.png")));
        btnRemove.setToolTipText("Remove the selected workflow");
        btnRemove.setEnabled(false);
        btnRemove.setMnemonic(KeyEvent.VK_E);
        btnRemove.setActionCommand("Remove");
        btnRemove.addActionListener(graphActionListener);
        GridBagConstraints gbc_btnRemove = new GridBagConstraints();
        gbc_btnRemove.anchor = GridBagConstraints.WEST;
        gbc_btnRemove.insets = new Insets(0, 0, 5, 30);
        gbc_btnRemove.gridx = 1;
        gbc_btnRemove.gridy = 0;
        add(btnRemove, gbc_btnRemove);

        btnCopy = new JButton();
        btnCopy.setIcon(new ImageIcon(getClass().getResource("/images/copy.png")));
        btnCopy.setToolTipText("Copy the selected workflow");
        btnCopy.setEnabled(false);
        btnCopy.setMnemonic(KeyEvent.VK_C);
        btnCopy.setActionCommand("Copy");
        btnCopy.addActionListener(graphActionListener);
        GridBagConstraints gbc_btnCopy = new GridBagConstraints();
        gbc_btnCopy.anchor = GridBagConstraints.WEST;
        gbc_btnCopy.insets = new Insets(0, 0, 5, 30);
        gbc_btnCopy.gridx = 2;
        gbc_btnCopy.gridy = 0;
        add(btnCopy, gbc_btnCopy);
        
        btnPaste = new JButton();
        btnPaste.setIcon(new ImageIcon(getClass().getResource("/images/paste.png")));
        btnPaste.setToolTipText("Paste the copied workflow");
        btnPaste.setEnabled(false);
        btnPaste.setMnemonic(KeyEvent.VK_P);
        btnPaste.setActionCommand("Paste");
        btnPaste.addActionListener(graphActionListener);
        GridBagConstraints gbc_btnPaste = new GridBagConstraints();
        gbc_btnPaste.anchor = GridBagConstraints.WEST;
        gbc_btnPaste.insets = new Insets(0, 0, 5, 5);
        gbc_btnPaste.gridx = 3;
        gbc_btnPaste.gridy = 0;
        add(btnPaste, gbc_btnPaste);
        
        graph = new Graph();
        graph.setAllowDanglingEdges(false);
        graph.setAllowLoops(false);
        graph.setAutoSizeCells(true);
        graph.setCellsEditable(false);
        graph.setCellsResizable(false);
        graph.getSelectionModel().setSingleSelection(true);
        graph.getStylesheet().getDefaultEdgeStyle().put(mxConstants.STYLE_EDGE, mxEdgeStyle.ElbowConnector);
        
        Document document = mxDomUtils.createDocument();
        inPort = document.createElement("InPort");
        outPort = document.createElement("OutPort");
        
        mxMultiplicity[] multiplicities = new mxMultiplicity[4];

        // OutPort cannot have any incoming connections
        multiplicities[0] = new mxMultiplicity(false, "OutPort", null, null, 0, "0", null, null, null, true);
        
        // InPort cannot have any outgoing connections
        multiplicities[1] = new mxMultiplicity(true, "InPort", null, null, 0, "0", null, null, null, true);
        
        // TODO I think these are applied on an individual edge basis, so have to find a new way to do overall connections
        
        // InPort can only have 1 incoming connection
        multiplicities[2] = new mxMultiplicity(false, "InPort", null, null, 0, "1", null, null, null, true);
        
        // OutPort can only have 1 maximum outgoing connection
        multiplicities[3] = new mxMultiplicity(true, "OutPort", null, null, 0, "1", null, null, null, true);

        graph.setMultiplicities(multiplicities);
        
        inGeometry = new mxGeometry(0, 0.5, 20, 20);
        inGeometry.setOffset(new mxPoint(-10, -10));
        inGeometry.setRelative(true);
        
        outGeometry = new mxGeometry(1.0, 0.5, 20, 20);
        outGeometry.setOffset(new mxPoint(-10, -10));
        outGeometry.setRelative(true);
        
        try
        {
            graph.getModel().beginUpdate();
            
            mxCell startCell = (mxCell)graph.insertVertex(graph.getDefaultParent(), null, "Start", 10, 10, 100, 100, "shape=triangle;perimeter=trianglePerimeter");
            startCell.setConnectable(false);
            
            mxCell startRightPort = new mxCell(outPort, outGeometry, "shape=ellipse;perimter=ellipsePerimeter");
            startRightPort.setVertex(true);
            
            graph.addCell(startRightPort, startCell);
        }
        finally
        {
            graph.getModel().endUpdate();
        }
        
        graphComponent = new mxGraphComponent(graph);
        graphComponent.setToolTips(true);
        graphComponent.getGraphControl().addMouseListener(graphMouseAdapter);
        GridBagConstraints gbc_graphComponent = new GridBagConstraints();
        gbc_graphComponent.fill = GridBagConstraints.BOTH;
        gbc_graphComponent.insets = new Insets(0, 10, 10, 10);
        gbc_graphComponent.gridwidth = 4;
        gbc_graphComponent.gridx = 0;
        gbc_graphComponent.gridy = 1;
        add(graphComponent, gbc_graphComponent);
    }
    
    /**
     * 
     * @author BDS
     */
    private class GraphActionListener implements ActionListener
    {
        /**
         * 
         */
        @Override
        public void actionPerformed(ActionEvent actionEvent)
        {
            if (actionEvent.getActionCommand().equals(btnAdd.getActionCommand()))
            {
                mxCell newCell = (mxCell)graph.insertVertex(graph.getDefaultParent(), null, "Hello,", 300, 300, 100, 100, "shape=diamond;perimeter=diamondPerimeter");
                newCell.setConnectable(false);
                graph.addCell(newCell);
                
                mxCell newCellLeftPort = new mxCell(inPort, inGeometry, "shape=ellipse;perimter=ellipsePerimeter");
                newCellLeftPort.setVertex(true);
                
                mxCell newCellRightPort = new mxCell(outPort, outGeometry, "shape=ellipse;perimter=ellipsePerimeter");
                newCellRightPort.setVertex(true);
                
                graph.addCell(newCellLeftPort, newCell);
                graph.addCell(newCellRightPort, newCell);
            }
            else if (actionEvent.getActionCommand().equals(btnRemove.getActionCommand()))
            {
                graph.removeCells(new Object[] { graph.getSelectionCell() }, true);
            }
            else if (actionEvent.getActionCommand().equals(btnCopy.getActionCommand()))
            {
                
            }
            else if (actionEvent.getActionCommand().equals(btnPaste.getActionCommand()))
            {
                
            }
        }
    }
    
    private class GraphMouseAdapter extends MouseAdapter
    {
        public void mouseReleased(MouseEvent mouseEvent)
        {
            btnRemove.setEnabled(graph.getSelectionCell() != null);
        }
    }

    private class Graph extends mxGraph
    {
        // Ports are not used as terminals for edges, they are
        // only used to compute the graphical connection point
        public boolean isPort(Object cell)
        {
            mxGeometry geo = getCellGeometry(cell);
            
            return (geo != null) ? geo.isRelative() : false;
        }
        
        // Implements a tooltip that shows the actual
        // source and target of an edge
        public String getToolTipForCell(Object cell)
        {
            if (model.isEdge(cell))
            {
                return convertValueToString(model.getTerminal(cell, true)) + " -> " +
                    convertValueToString(model.getTerminal(cell, false));
            }
            
            return super.getToolTipForCell(cell);
        }
        
        // Removes the folding icon and disables any folding
        public boolean isCellFoldable(Object cell, boolean collapse)
        {
            return false;
        }
        
        public boolean isCellMovable(Object cell)
        {
            return !((mxCell)cell).isEdge() && !isPort(cell);
        }
    }
}
